﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using Sio.Mdm.Server;
using Sio.Mdm.Client;

namespace Sio.Mdm.Entities
{
    [AttributeUsage(AttributeTargets.Property, AllowMultiple=true)]
    public abstract class ValidationAttribute : Attribute
    {
        #region Properties

        #region ValidationState
        private EntityState _state = EntityState.Unknown;
        /// <summary>
        /// Get or set state of <see cref="IEntity"/> that this validator
        /// is for.
        /// </summary>
        public EntityState ValidationState
        {
            get
            {
                return _state;
            }
            set
            {
                _state = value;
            }
        }
        #endregion

        #endregion

        /// <summary>
        /// Called when validation is needed
        /// </summary>        
        /// <param name="args">Arguments to validate</param>
        /// <returns>True if <paramref name="value"/> is valid in givent context</returns>
        public abstract bool Validate(IEntity entity, string fieldName, object fieldValue);
    }

    public class NullOrEmptyValidationAttribute : ValidationAttribute
    {
        public override bool Validate(IEntity entity, string fieldName, object fieldValue)
        {
            if (fieldValue is String)
            {
                return !String.IsNullOrEmpty(fieldValue as string);
            }

            return fieldValue != null;
        }
    }

    public class RegexValidationAttribute : ValidationAttribute
    {
        private Regex Regex;

        public RegexValidationAttribute(string pattern)
        {
            Regex = new Regex(pattern);
        }
        public RegexValidationAttribute(Regex regex)
        {
            Regex = regex;
        }

        public override bool Validate(IEntity entity, string fieldName, object fieldValue)
        {   
            return fieldValue == null? 
                false: Regex.IsMatch(fieldValue.ToString());
        }
    }

    public class EmailValidationAttribute : RegexValidationAttribute
    {
        /// <summary>
        /// Yeah, I know ... probably the most complicated regullar expression
        /// you ever saw to evaluate mail address ;)
        /// </summary>
        public const string EmailPattern =
                @"^(([\w-]+\.)+[\w-]+|([a-zA-Z]{1}|[\w-]{2,}))@"
              + @"((([0-1]?[0-9]{1,2}|25[0-5]|2[0-4][0-9])\.([0-1]? [0-9]{1,2}|25[0-5]|2[0-4][0-9])\."
              + @"([0-1]?[0-9]{1,2}|25[0-5]|2[0-4][0-9])\.([0-1]?[0-9]{1,2}|25[0-5]|2[0-4][0-9])){1}|"
              + @"([a-zA-Z]+[\w-]+\.)+[a-zA-Z]{2,4})$";


        public EmailValidationAttribute() :
            base(EmailPattern)
        {
        }

        public override bool Validate(IEntity entity, string fieldName, object fieldValue)
        {            
            return base.Validate(entity, fieldName, fieldValue);
        }
    }

    public class RangeValidationAttribute : ValidationAttribute
    {
        public RangeValidationAttribute(IComparable minValue, IComparable maxValue)
            :this(minValue, maxValue, false)
        {
        }
        public RangeValidationAttribute(IComparable minValue, IComparable maxValue, bool allowNull)
        {            
            if (_minValue == null || _maxValue == null)
            {
                throw new ArgumentException("min or max value cannot be null");
            }

            AllowNull = allowNull;
            _minValue = minValue;
            _maxValue = maxValue;
        }

        private bool _allowNull = false;
        public bool AllowNull
        {
            get
            {
                return _allowNull;
            }
            set
            {
                _allowNull = value;
            }
        }

        private IComparable _minValue;
        public IComparable MinValue
        {
            get
            {
                return _minValue;
            }            
        }
        private IComparable _maxValue;
        public IComparable MaxValue
        {
            get
            {
                return _maxValue;
            }
        }

        public override bool Validate(IEntity entity, string fieldName, object fieldValue)
        {
            if (fieldValue == null)
            {
                return AllowNull;
            }

            return _minValue.CompareTo(fieldValue) < 0 && _maxValue.CompareTo(fieldValue) > 0;
        }
    }

    public class ServiceValidationAttribute : ValidationAttribute
    {
        /// <summary>
        /// Construct service validation attribute
        /// </summary>
        /// <param name="serviceType">Name of interface implmeneting <see cref="IService"/> type</param>
        /// <param name="methodName">A method name to call on service</param>
        /// <param name="returnVal">A value returned from service that means positive validation</param>
        public ServiceValidationAttribute(string serviceName, string methodName, Type returnValueType)            
        {
            _serviceName = serviceName;
            _methodName = methodName;
            _returnVal = returnValueType;
        }
        /// <summary>
        /// Construct service validation attribute
        /// </summary>
        /// <param name="serviceType">Asembly qualified name of interface implmeneting <see cref="IService"/> type</param>
        /// <param name="methodName">A method name to call on service</param>
        /// <param name="returnVal">A value returned from service that means positive validation</param>
        public ServiceValidationAttribute(Type serviceType, string methodName, object returnVal)
        {
            _serviceType = serviceType;
            _methodName = methodName;
            _returnVal = returnVal;
        }

        private string _serviceName;
        private Type _serviceType;
        public Type ServiceType
        {
            get
            {
                return _serviceType;
            }
        }

        private string _methodName;
        public string MethodName
        {
            get
            {
                return _methodName;
            }
        }

        private object _returnVal;
        public object ReturnValue
        {
            get
            {
                return _returnVal;
            }
            set
            {
                _returnVal = value;
            }
        }

        public override bool Validate(IEntity entity, string fieldName, object fieldValue)
        {
            IService service = ServiceFactory.GetService(ServiceType);

            try
            {
                return service.GetType().InvokeMember(
                    MethodName,
                    System.Reflection.BindingFlags.Public,
                    null,
                    service,
                    new object[] {entity, Sio.Mdm.Types.Session.Current }
                ) == ReturnValue;
            }
            catch (Exception ex)
            {
                Sio.Mdm.Utils.Logger.WriteError("Error validation parameter when calling service {0}({1})", ServiceType.Name, MethodName);
                Sio.Mdm.Utils.Logger.WriteException(ex);
                return false;
            }
        }
    }
}
